package avicit.bdp.dds.api.service;

import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.service.dto.BdpColumnDTO;
import avicit.bdp.common.service.dto.BdpTableDTO;
import avicit.bdp.common.service.dto.DataSourceDTO;
import avicit.bdp.common.service.service.DataQueryService;
import avicit.bdp.common.service.service.DataSourceService;
import avicit.bdp.common.utils.BdpLogUtil;
import avicit.bdp.common.database.ColumnMeta;
import avicit.bdp.common.utils.database.DBUtils;
import avicit.bdp.common.database.TableMeta;
import avicit.bdp.core.enums.TableTypeEnum;
import avicit.bdp.core.poi.util.csv.ExcelUtils;
import avicit.bdp.dds.api.dto.TableSelectDto;
import avicit.bdp.dds.common.Constants;
import avicit.bdp.dds.dao.entity.Project;
import avicit.bdp.dds.dao.entity.ProjectTable;
import avicit.bdp.dds.dao.mapper.DataModelMapper;
import avicit.bdp.dds.dao.mapper.ProjectMapper;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.core.exception.BusinessException;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author mayanj
 */
@Service
public class ModelService extends BaseService<DataModelMapper, BdpTableDTO> {

	private static final Logger logger = LoggerFactory.getLogger(ModelService.class);

	@Autowired
	private ProjectTableService projectTableService;

	@Autowired
	private DataSourceService dataSourceService;

	@Autowired
	private ModelColumnService modelColumnService;

	@Autowired
	private DataQueryService dataQueryService;
	@Autowired
	private ProjectMapper projectMapper;

	/**
	 * 数据源模型导入
	 *
	 * @param dataSourceId 数据源ID
	 * @param tables       表ID集合
	 * @param projectId    项目ID
	 * @param typeId       分类ID
	 */
	@Transactional(rollbackFor = Exception.class)
	public Map<String, List<String>> importModel(String dataSourceId, String tables, String projectId, String typeId) {
		DataSourceDTO dataSourceDTO = dataSourceService.queryDataSourceById(dataSourceId);
		if (dataSourceDTO == null) {
			throw new BusinessException("数据源不存在");
		}

		Map<String, List<String>> result = new HashMap<>(2);
		List<String> successList = new LinkedList<>();
		List<String> errorList = new LinkedList<>();
		result.put("successList", successList);
		result.put("errorList", errorList);

		Project project = projectMapper.selectByPrimaryKey(projectId);

		String[] tableArr = tables.split(Constants.COMMA);
		if (tableArr.length > 0) {
			for (String tableName : tableArr) {
				BdpTableDTO dataModel = new BdpTableDTO();
				dataModel.setId(ComUtil.getId());
				dataModel.setName(tableName);
				dataModel.setCnName(tableName);
				dataModel.setTypeId(typeId);
				dataModel.setType(TableTypeEnum.TABLE_DATA_DEVELOP_MODELING.getCode());
				dataModel.setDataSourceId(dataSourceId);
				dataModel.setDataSourceType(dataSourceDTO.getDatasourceType());
				//导入的都已经是落库的
				dataModel.setStatus(3);
				dataModel.setSecretLevel(project.getSecretLevel());
				try {
					//获取列信息,并保存
					TableMeta tableMeta = DBUtils.getTable(DBUtils.getBaseDataSource(dataSourceDTO), tableName);
					if (tableMeta != null) {
						List<ColumnMeta> columnMetaList = tableMeta.getColumnMetaData();
						//处理iotDb Time字段
						if (avicit.bdp.core.constant.Constants.DB_TYPE_IOTDB.equals(dataSourceDTO.getDatasourceType())) {
							ColumnMeta columnMeta = new ColumnMeta();
							columnMeta.setName("Time");
							columnMeta.setCnName("Time");
							columnMeta.setColumnType("timestamp");
							columnMeta.setNullable(1);
							columnMetaList.add(0, columnMeta);
							int orderBy = 1;
							for (ColumnMeta meta : columnMetaList) {
								meta.setOrderNo(orderBy);
								orderBy++;
							}
						}
						//处理排序
						columnMetaList.parallelStream().forEach(columnMeta -> {
							BdpColumnDTO column = new BdpColumnDTO();
							column.setTableId(dataModel.getId());
							column.setName(columnMeta.getName());
							column.setCnName(columnMeta.getCnName());
							column.setRemark(columnMeta.getRemark());
							column.setColumnLength(columnMeta.getColumnLength());
							column.setColumnPrecision(columnMeta.getColumnPrecision());
							column.setColumnType(columnMeta.getColumnType());
							column.setNullable(columnMeta.getNullable());
							column.setOrderBy(columnMeta.getOrderNo());
							modelColumnService.insertBdpDasColumn(column);
						});
					} else {
						logger.error("项目[{}]数据源[{}]导入模型[{}]，没有获取到表信息",
								projectId, dataSourceId, tableName);
						errorList.add(tableName);
						continue;
					}

					//保存模型
					this.insertSelective(dataModel);
					//记录日志
					BdpLogUtil.log4Insert(dataModel);

                    //保存关系
                    ProjectTable projectTable = new ProjectTable();
                    projectTable.setId(ComUtil.getId());
                    projectTable.setProjectId(projectId);
                    projectTable.setTableId(dataModel.getId());
                    projectTable.setTypeId(typeId);
                    //记录日志
                    /******数据源导入也算作新增*******/
                    projectTable.setImported(ProjectTable.IMPORTED_NEW);
                    BdpLogUtil.log4Insert(projectTable);
                    projectTableService.insertSelective(projectTable);

					successList.add(tableName);
				} catch (Exception e) {
					errorList.add(tableName);
					logger.error("项目[{}]数据源[{}]导入模型[{}]，异常{}",
							projectId, dataSourceId, tableName, e);
				}
			}
		}
		return result;
	}

	/**
	 * 数据源模型导入，查询数据源下的表，并查询表的导入和入库情况
	 * 数据源选择显示的表，需要判断是否已经导入过，或是已经存在，已经存在的需要设置为不可选
	 * 公共模型里没有落库的表不允许选
	 *
	 * @param dataSourceId 数据源ID
	 * @param projectId    项目ID
	 * @param typeId       分类ID
	 */
	public List<TableSelectDto> queryTableAndStatus(String dataSourceId, String projectId, String typeId) throws Exception {
		//查询数据源下面所有的表
		List<TableMeta> tableMetaList = dataSourceService.getTableListByDatasourceId(dataSourceId, false);
		if (!CollectionUtils.isEmpty(tableMetaList)) {
			List<String> tables = tableMetaList.stream().map(TableMeta::getName).collect(Collectors.toList());

			//查询导入表
			List<String> importedTableList = projectTableService.queryTableName(projectId, dataSourceId, typeId);

			//TODO 查询公共模型里没有落库的表

			return tables.parallelStream().map(item -> {
				TableSelectDto dto = new TableSelectDto();
				dto.setTableName(item);
				if (!CollectionUtils.isEmpty(importedTableList) && importedTableList.contains(item)) {
					dto.setEnable(false);
				}
				return dto;
			}).collect(Collectors.toList());
		}
		return null;
	}


	/**
	 * 查询数据库表的数据
	 *
	 * @param tableId      模型ID
	 * @param dataSourceId 数据源ID
	 * @param sql          sql
	 * @param pageNum      pageNum
	 * @param pageSize     pageSize
	 * @return 数据库表数据
	 */
	public Map<String, Object> getDataListByTable(String tableId, String dataSourceId, String sql,
												  Integer pageNum, Integer pageSize) {

		if (StringUtils.isNotBlank(tableId)) {
			BdpTableDTO tableDTO = this.mapper.selectByPrimaryKey(tableId);
			//获取列信息
			if (tableDTO != null) {
				BdpColumnDTO columnQuery = new BdpColumnDTO();
				columnQuery.setTableId(tableId);
				List<BdpColumnDTO> columnList = modelColumnService.selectList(columnQuery);
				tableDTO.setColumnList(columnList);
				return dataQueryService.getDataListByTable(tableDTO, null, null, null, pageNum, pageSize);
			}
		}

		return dataQueryService.getDataListByTable(null, dataSourceId, sql, null, pageNum, pageSize);
	}

	public void downloadTableData(String tableId, HttpServletResponse response) {
		Map<String, Object> dataMap = this.getDataListByTable(tableId, null, null, 1, 100);
		if (dataMap != null) {
			ExcelUtils excelUtils = new ExcelUtils();
			try {
				List<BdpColumnDTO> columnList = (List<BdpColumnDTO>) dataMap.get("columnList");
				//表头的集合，用于添加表头
				List<ExcelExportEntity> entityList = new ArrayList<>();
				for (BdpColumnDTO column : columnList) {
					//列头由参数汉字名称,参数key为列key
					entityList.add(new ExcelExportEntity(column.getName(), column.getName(), 30));
				}

				List<Map<String, Object>> dataList = (List<Map<String, Object>>) dataMap.get("resultList");

				BdpTableDTO modelInfo = (BdpTableDTO) dataMap.get("modelInfo");

				Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(modelInfo.getName(),
						modelInfo.getName()), entityList, dataList);
				excelUtils.downLoadExcel(modelInfo.getName(), workbook, response);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 工作台导入表前校验
	 *
	 * @param
	 * @return
	 */
	public String validateModelName(String tables, String projectId, String typeId) {
		String[] tableNames = tables.split(Constants.COMMA);
		if (org.apache.commons.lang3.StringUtils.isAnyBlank(typeId, projectId)) {
			throw new BusinessException("请检查参数!");
		}

		String errorInfo = "";
		for (String tableName : tableNames) {
			int count = projectTableService.selectModelByTypeId(tableName, projectId, typeId);
			if (count > 0) {
				errorInfo += "【" + tableName + "】\n";
			}
		}
		if (StringUtils.isNotBlank(errorInfo)) {
			errorInfo = "以下表在选择分类中存在重复表名，请重新选择" + errorInfo;
		}
		return errorInfo;
	}
}
